/**
 * @brief DTU虚拟硬件，将各个软件模块和硬件模块组合在一起的模块。这个模块
 *     整个DTU的核心汇总模块。
 * @version 1.0
 * @date 2017-2-27
 * @author 宋炜
*/
#ifndef DTU_H
#define DTU_H

#include <atomic>
#include <string>
#include <vector>

#include "config.hpp"
#include "defines.hpp"
#include "modbus.hpp"
#include "filter.hpp"
#include "serial.hpp"
#include "serialio.hpp"
#include "at.hpp"
#include "sys_var.hpp"
#include "http.hpp"
#include "tcp.hpp"
#include "sms.hpp"
#include "smsio.hpp"

#if defined( __USE_MEMPOOL__ )
#   include <jemalloc/jemalloc.h>
#endif

class dtu
{
public:
   	 /* 定义错误代码 */
	enum err_code{
		ERR_BASE = ERR_VDTU,
		ERR_FIND_CONFIG_FILE,                    // 找不到配置文件
		ERR_OPEN_CONFIG,                         // 打开配置文件失败
		ERR_CONF_OBJ,                            // 系统变量对象无效
		ERR_INIT_SERIAL_0 ,                      // 串口0初始化失败
		ERR_INIT_SERIAL_1 ,
		ERR_INIT_SERIAL_2 ,
		ERR_INIT_SERIAL_3 ,
		ERR_WKMOD_NOT_SUPPORTED,                 // 不支持的运行模式
		OK = 0
	};
	enum wkmod{ NET = 10 , HTTPD , SMS , WK_MQTT , TCPSSL , HTTPS , MQTTSSL };
	/* SOCKET 协议类型 */
	enum sock_type{ TCP = 20 , UDP , UDT , SOCK_UNKNOWN };
	/*
	*  网络类型定义。主要的应用内容方便检索对端，
	*/
	enum net_type{ SOCKET , HTTP , MQTT };

	struct stNet{
		std::atomic< int >      index;             // A,B,C,D分别占据1，2 ，3,4， 0 留给signalcloud
		std::atomic< int >      serial_id;         // 网口ID
		std::atomic< bool >     locked;            // 是否被锁定；
		std::atomic< net_type > type;              // 网口类型，网口分成套接字接口，HTTP接口，MQTT接口三种
		std::atomic< int >      m_short_or_long;   // 长短链接
		std::shared_ptr< filterIO > pt_socket;         // socket
		CTimer                  m_reconnect;       // 如果是短连接则是保持时间; 如果是长连接是断开后的重连时间

		stNet(){}
		stNet( const stNet& b )
		{
			index = b.index.load();
			serial_id = b.serial_id.load();
			locked = b.locked.load();
			type = b.type.load();
			pt_socket = b.pt_socket;
		}

		stNet& operator=( const stNet & b )
		{
			index = b.index.load();
			serial_id = b.serial_id.load();
			locked = b.locked.load();
			type = b.type.load();
			pt_socket = b.pt_socket;

			return *this;
		}
	};
	/*
	* 串口设备定义。
	*/
	struct stCOM{
		std::string               aliase;           // 串口别名，比如COM0,COM1,COM2,COM3,COM4
		std::string               name;             // 串口正式名称
		std::atomic<int>          fd;               // 端口文件描述，如果端口没有打开则是-1
		std::atomic< bool >       locked;           // 是否锁定，true锁定，false解锁
		std::shared_ptr< serialIO >    ptserial;    // 串口过滤器
		/* 过滤器链：
		* 0 , AT指令链
		* 1 , 串口到SOCKET 1 链
		* 2 , 串口到SOCKET 2 链
		* 3 , 串口到SOCKET 3 链
		* 5 , 串口到SOCKET 4 链
		*/
		filterIO::afilterlist_t   ftbl;             // 过滤器列表

		stCOM():ftbl( 5 ) { }

		stCOM( const stCOM& b )
		{
			aliase   = b.aliase;
			name     = b.name;
			fd       = b.fd.load();
			locked   = b.locked.load();
			ptserial = b.ptserial;
			ftbl     = b.ftbl;
		}
        
		stCOM( stCOM&& b )
		{
			aliase   = std::move( b.aliase );
			name     = std::move( b.name );
			fd       = b.fd.load();
			locked   = b.locked.load();
			ptserial = b.ptserial;
			ftbl     = std::move( b.ftbl );
		}
		virtual ~stCOM(){}

		stCOM& operator=( const stCOM& b )              //
		{
			aliase   = b.aliase;
			name     = b.name;
			fd       = b.fd.load();
			locked   = b.locked.load();
			ptserial = b.ptserial;
			ftbl     = b.ftbl;

			return *this;
		}

		stCOM& operator=( stCOM&& b )                   //
		{
			return *this;
		}
	};
private:
	std::vector< stCOM >        m_coms;              // 串口登记表
	std::vector< stNet >        m_nets;              // 网口登记表
	//std::shared_ptr<devManage >　p_dev;		 // 设备管理
	/*std::vector< modbus *>      m_modbus_tb;*/         // modbus-rtu 自采集器。这里记录器指针，方便对其进行暂停或者恢复操作
	/* 过滤器链：
	* 0 , AT指令链
	* 1 , 串口到SOCKET 1 链
	* 2 , 串口到SOCKET 2 链
	* 3 , 串口到SOCKET 3 链
	* 4 , 串口到SOCKET 4 链
	* 5 , HTTP 0 链条
	* 6 , 串口到HTTP过滤器链
	* 7 , 短信AT指令
	*/
	std::vector< std::shared_ptr<filterList> >    m_filter_lists;      // 过滤器链记录表
	/*
	* 下面定义的参数是和有人7S4兼容的设置数据内容，这些内容会影响到A,B,C,D四个端口。
	* 在1,2,3,4四个SOCKET连接中还可以使用独立的设置。但是如果是重叠的功能，比如心跳功能设置，不论是全局开关还是独立开
	* 关只要有一个开关开启，就会导致功能开启
	*/
	std::string                 m_sn;                // 设备串号
	std::atomic< wkmod >        m_wkmod;             // 工作模式
	std::string                 m_at_pswd;           // AT 指令密码
	std::string                 m_start_msg;         // 启动信息
	std::atomic< int >          m_restart_intval;    // 自动重启等候时间

	std::atomic< bool  >        m_heart_en;          // 是否开启心跳包，只对A,B,C,D四个网口有效。其他网口需要分别设置
	std::string                 m_heart_dt;          // 心跳包数据
	std::atomic< bool  >        m_heart_tcp;         // 是否使用TCP keepalive功能，如果使用的是TCP链接，打开这个功能会定时

	// 服务器发送keepalive 数据包。使用这个功能不会造成数据包中混杂数据，方便用户再上位机解析数据
	std::atomic< long >         m_kplv_itvl;         // tcp keepalive发包间隔
	// 一下定义AT指令引擎
	std::shared_ptr< atCom >    m_ptAtSerial;        // 串口AT
	std::shared_ptr< atNet >    m_ptAtNet;           // 网络AT指令
	std::shared_ptr< atSMS >    m_ptAtSMS;           // 网络AT指令
	// 一下是系统运行状态监视计时器
	CTimer                      m_net_demon_timer;   // 网络状态监控程序
	//CTimer                      m_restart_timer;
	CTimer			    	    m_auto_boot_timer;	 // 自动重启计时器
	std::atomic< bool >         m_is_dail_ready;

public:
	dtu();
	virtual ~dtu();
	/**
	 * @brief 设置工作模式
	 */
	err_code  WKMOD( const std::string& wkmod );
	wkmod WKMOD(){ return m_wkmod; }
	/**
	 * @brief 检查socket是否连接
	 * @param index ， socket 的索引
	 * @return 成功连接返回true, 否则返回false
	 */
	bool IsSockConnect( int index );
	/**
	 * @brief 判断串口是否正在操作
	 */
	bool is_locked( int fd );
	/**
	 * @brief 重启应用
	 */
	void reboot_this();
	/**
	 * @brief 重启操作系统
	 */
	void reboot_sys();
	/**
	 * @brief 硬件复位
	 */
	void reset_chip();
	/**
	 * @brief 启动DTU控制循环
	 */
	void Run();
	/**
	 * @brief 获取或者设置心跳包开关状态
	 * @param sw , true 开启心跳；false 关闭心跳
	 * @param index ，套接字索引编号 1 代表A ，2 代表B, 3 代表C, 4代表D
	 * @return 心跳开启返回true , 否则返回
	 * @note 没有index的函数会同时试着ABCD四个socket。当四个socket的设置不一致的时候获取参数抛出 ERR_HEART_BROKEN异常
	 * @exception ERR_HEART_BROKEN , 仅仅对于无index的获取心跳开关的函数有效
	 */
	void HeartEnable( bool sw );
	void HeartEnable( int index , bool sw );
	bool HeartEnable( int index );
	bool HeartEnable();
	/**
	 * @brief 设置或者读取心跳数据的参数
	 * @param data ，心跳包数据。可以是任何非0字符串，长度不要超过100个字节
	 * @param index ，socket索引
	 * @return 获取数据返回心跳数据，不包含"<"和">"
	 * @note 心跳数据是以"<"和">"括起来的字符串数据
	 * @exception ERR_HEART_BROKEN , 仅仅对于无index的获取心跳开关的函数有效
	 */
	void HeartData( const std::string& data );
	std::string HeartData( );
	void HeartData( int index , const std::string& data );
	std::string HeartData( int index );
	/**
	 * @brief 注册包开关状态读取或者设置
	 */
	void RegEnable( bool sw );
	bool RegEnable();
	bool RegEnable( int index );
	void RegEnable( int index , bool sw );
	/**
	 * @brief 注册包数据读取或者设置
	 */
	void RegData( const std::string& data );
	std::string RegData();
	void RegData( int index , const std::string& data );
	std::string RegData( int index ) ;
	/**
	 * @brief 设置串口参数
	 * @param index ， 串口索引
	 * @param baud , 波特率。波特率必须使用指定范围内的波特率
	 * @param char_len , 字符长度
	 * @param s , 停止位
	 * @param p , 校验方式
	 * @param f , 流控，分成硬件流控和软件流控
	 */
	void SetSerial( int index ,int baud ,int char_len ,serial::stop s ,serial::parity p ,serial::flow f );
	void GetSerial( int index ,int &baud ,int &char_len ,serial::stop &s ,serial::parity &p,serial::flow &f);
	/**
	 * @brief 暂停MODBUS采集任务
	 */
	void PauseModbus(){}
	/**
	 * @brief 恢复MODBUS采集操作
	 */
	void RestoreModbus(){}

	/**
	 * @brief 设置或者读取HTTP参数
	 * @param server HTTP服务器
	 * @param port 服务器监听端口
	 * @param url http资源路径
	 * @param mtd 提交方式
	 */
	void SetHTTP( const std::string& server , int port , const std::string& url , http::mtd mtd );
	void GetHTTP( std::string& server , int &port , std::string& url , http::mtd & mtd );
	/**
	 * @brief 设置HTTP提交开关或者。
	 * @note HTTP连接不占用socket ABCD, 使用独立socket连接。
	 */
	void Http( bool sw );
	bool Http( );
	/**
	 * @brief 启动SOCKET连接。对于TCP建立SOCKET然后连接服务器，对于UDP协议仅仅创建SOCKET并启动读取
	 *        操作。
	 * @param index 套接字索引
	 * @param type
	 * @return 成功操作返回OK，否则返回错误代码
	 * @note 注意：
	 *     (1) 这个函数启动异步连接操作，当函数的返回的时候并不意味这已经完成的连接。
	 *     (2) UDT协议暂时不实现。
	 */
	err_code Connect( int index , sock_type type );
	/**
	 * @brief 允许或者关停指定索引的socket
	 * @param index ， socket索引从1开始
	 * @param sw , true 开启；false关闭
	 * @return 如果socket开启返回true，否则返回false
	 */
	void SocketEnable( int index , bool sw );
	bool SocketEnable( int index );
	/**
	 * @brief 设置socket参数
	 * @param index
	 * @param server 服务器地址，可以使域名也可以是IP地址
	 * @param port 服务器端口
	 * @note v1.0不支持IPV6
	 */
	void Socket( int index , const std::string& server , int port );
	/**
	 * @brief 获取socket的链接状态
	 * @param index
	 * @return
	 */
	bool Link( int index );
	/**
	 * @brief MODBUS-RTU 开关设置和查询操作
	 */
	void Modbus( bool sw );
	bool Modbus( );
	/**
	 * @brief 查询或者设置MODBUS采集任务间隔
	 * @param ms ， 采集任务间隔时间单位为ms
	 * @param overtime , 通讯超时时间，在这个时间内不会发送第二个采集指令。
	 */
	void ModbusIntv( long ms , long overtime );
	void ModbusIntv( long * ms , long * overtime );
	/**
	 * @brief 添加或者删除MODBUS-RTU采集任务。这里需要注意MODBUS-RTU目前仅仅支持0x03的采集指令
	 * @param site ， 站点编号
	 * @param reg_start ， 其实寄存器号码
	 * @param count ， 采集的积存器数量
	 * @return 成功返回操作任务的索引
	 */
	int AddMBMission( int site , int reg_start , int count );
	/**
	 * @brief 获取或者设置密码
	 * @param pswd
	 * @return
	 */
	std::string ATPswd();
	err_code ATPswd( const std::string& pswd );
	/**
	 * @brief 设置启动信息
	 * @param msg
	 * @return
	 */
	err_code StartMsg( const std::string& msg );
	std::string StartMsg( );
	/**
	 * @brief
	 */
	err_code RestartIntval( long itvl );
	long RestartIntval(){ return m_restart_intval.load(); }
	std::string SN(){ return m_sn; }
	err_code SN( const std::string& sn );
	/**
	 * @brief 取IMEI
	 */
	std::string GetIMEI();
	/**
	 * @brief 取ICCID
	 */
	std::string GetICCID();
	/**
	 * @brief 取DTU运行错误代码
	 * @return 运行错误代码
	 */
	int GetErrorCode();
private:
	/**
	 * @brief 结束DTU操作
	 */
	void stop();
	/**
	 * @brief 根据产品类型设置可以使用的串口数量初始化串口设备列表。
	 * @note 这个函数初始化串口数量后后续运行过程中不会再添加串口，主要用于
	 *      不同型号的产品进行设备初始化限定。因此对应的串口数量在程序编译的
	 *      过程确定。
	 * @exception , 操作错误抛出对应的代码
	 */
	void init_serial_port_tbl();
	/**
	 * @brief 初始化SMS服务
	 */
	void init_sms();
	/**
	 * @brief 判断拨号是否就绪。
	 * @return 拨号就绪返回true ， 否则返回false
	 */
	bool is_dail_ready();
	/**
	 * @brief 初始化MODBUS-RTU采集程序
	 */
#if ENABLE_MODBUS == 1
    	void init_modbus ( std::shared_ptr<CSysVar> ptvar ) ;
#endif
	/**
	 * @brief 初始化加密处理程序
	 */
	void init_crypto(std::shared_ptr<CSysVar> ptvar ) ;
	/**
	 * @brief 初始化ssl接口
	 */
	void init_ssl(std::shared_ptr<CSysVar> ptvar ) ;
	/**
	 * @brief 初始化zip过滤器
	 */
	void init_zip(std::shared_ptr<CSysVar> ptvar );

	/**
	 * @brief 初始化TCP过滤器
	 */
	bool init_tcp(std::shared_ptr<CSysVar> ptvar );
	/**
	 * @brief 初始化UDP过滤器
	 */
	bool init_udp(std::shared_ptr<CSysVar> ptvar ) ;
	/**
	 * @brief 初始化HTTP过滤器
	 */
	void init_http(std::shared_ptr<CSysVar> ptvar );
#if ENABLE_MQTT == 1
	/**
	 * @brief 初始化MQTT过滤器
	 */
	void init_mqtt(std::shared_ptr<CSysVar> ptvar );
#endif
	/**
	 * @brief 初始化串口
	 */
	void init_serial( std::shared_ptr< CSysVar> ptval ) ;
	void init_serial_0( std::shared_ptr< CSysVar > ptval );
	void init_serial_1( std::shared_ptr< CSysVar > ptval );
	void init_serial_2( std::shared_ptr< CSysVar > ptval );
	/**
	 * @brief 初始化AT指令模块
	 */
	void init_at( std::shared_ptr< CSysVar> pt ) ;
	/**
	 * @brief 初始化串口设备表
	 */
	void init_serial_port_tbl( std::shared_ptr< CSysVar> ptval ) ;
	/**
	 */
	void init_wkmod( std::shared_ptr< CSysVar> ptval );
	/**
	 * @brief 设置工作模式
	 */
	err_code set_wkmod( std::shared_ptr< CSysVar > ptval , const std::string& mod );
	/**
	 * @brief 
	 */
	void init_at_passwd( std::shared_ptr< CSysVar> ptvar );
	/**
	 * @brief 
	 */
	err_code set_at_passwd( std::shared_ptr< CSysVar > ptvar , const std::string& pswd );
	/**
	 * @brief 初始化网络值守器
	 */
	void init_net_demon();
	/**
	 * @brief 允许SIM卡热插拔
	 */
	void enable_simHotInsert();
	/**
	 * @brief 𥘉始化设备管理器
	 */
	//void init_device_manager( );
	void init_auto_boot( std::shared_ptr< CSysVar > ptvar );
#if ENABLE_WIFI == 1 
    	void init_wifi();
#endif

#if ENABLE_PHY == 1 
    	int init_phy();
#endif
};
// //////////////////////////////////////////////////////////////////////////////////////////////////////////
// 
/**
 * @brief 工厂函数，创建一个DTU对象，并返回对象指针
 * @return 成功返回对象指针，否则返回nullptr
 */
extern dtu* GetOrCreateDTU();
/**
 * @brief 停止操作系统重启计时器
 */
extern void StopRebootTimer();
/**
 * @brief 启动操作系统重启计时器
 */
extern void StartRebootTimer();
/**
 * @brief 获取设备型号
 */
extern std::string GetDeviceType();
#endif // DTU_H
